<?php
namespace app\common\service;
class WechatPayV3
{
    private $mchid;          // 商户号
    private $apiV3Key;       // APIv3密钥
    private $serialNo;       // 商户证书序列号
    private $privateKey;     // 商户私钥

    public function __construct($config)
    {
        $this->mchid = $config['mchid'];
        $this->apiV3Key = $config['apiv3_key'];
        $this->serialNo = $config['serial_no'];
        $this->privateKey = openssl_get_privatekey(file_get_contents($config['private_key_path']));
    }

    /**
     * JSAPI下单
     * @param array $params 订单参数
     * @return array
     */
    public function jsapiOrder(array $params)
    {
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
        $data = [
            'appid' => $params['appid'],
            'mchid' => $this->mchid,
            'description' => $params['description'],
            'out_trade_no' => $params['out_trade_no'],
            'notify_url' => $params['notify_url'],
            'amount' => [
                'total' => $params['amount'],
                'currency' => 'CNY'
            ],
            'payer' => [
                'openid' => $params['openid']
            ]
        ];

        return $this->request($url, $data);
    }

    /**
     * 小程序下单
     * @param array $params 订单参数
     * @return array
     */
    public function miniProgramOrder(array $params)
    {
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
        $data = [
            'appid' => $params['appid'],
            'mchid' => $this->mchid,
            'description' => $params['description'],
            'out_trade_no' => $params['out_trade_no'],
            'notify_url' => $params['notify_url'],
            'amount' => [
                'total' => $params['amount'],
                'currency' => 'CNY'
            ],
            'payer' => [
                'openid' => $params['openid']
            ]
        ];

        return $this->request($url, $data);
    }

    /**
     * 发送请求
     * @param string $url 请求地址
     * @param array $data 请求数据
     * @return array
     */
    private function request($url, $data)
    {
        $body = json_encode($data, JSON_UNESCAPED_UNICODE);
        $timestamp = time();
        $nonce = $this->generateNonce();
        $sign = $this->sign('POST', $url, $timestamp, $nonce, $body);

        $headers = [
            'Content-Type: application/json',
            'Accept: application/json',
            'User-Agent: FastAdmin',
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $sign,
            'Wechatpay-Serial: ' . $this->serialNo
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            throw new Exception('微信支付请求失败: ' . $response);
        }

        return json_decode($response, true);
    }

    /**
     * 生成签名
     * @param string $method 请求方法
     * @param string $url 请求URL
     * @param int $timestamp 时间戳
     * @param string $nonce 随机字符串
     * @param string $body 请求体
     * @return string
     */
    private function sign($method, $url, $timestamp, $nonce, $body)
    {
        $urlParts = parse_url($url);
        $canonicalUrl = ($urlParts['path'] ?? '/') . (empty($urlParts['query']) ? '' : '?' . $urlParts['query']);

        $message = $method . "\n" .
            $canonicalUrl . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";

        openssl_sign($message, $signature, $this->privateKey, 'sha256WithRSAEncryption');
        $signature = base64_encode($signature);

        return sprintf(
            'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $this->mchid,
            $nonce,
            $timestamp,
            $this->serialNo,
            $signature
        );
    }

    /**
     * 生成随机字符串
     * @return string
     */
    private function generateNonce()
    {
        return md5(uniqid(mt_rand(), true));
    }

    /**
     * 获取支付参数（用于前端调起支付）
     * @param string $prepayId 预支付ID
     * @return array
     */
    public function getJsApiParams($prepayId)
    {
        $timestamp = time();
        $nonce = $this->generateNonce();
        $package = 'prepay_id=' . $prepayId;
        $message = "{$this->mchid}\n{$timestamp}\n{$nonce}\n{$package}\n";

        openssl_sign($message, $signature, $this->privateKey, 'sha256WithRSAEncryption');
        $paySign = base64_encode($signature);

        return [
            'timeStamp' => (string)$timestamp,
            'nonceStr' => $nonce,
            'package' => $package,
            'signType' => 'RSA',
            'paySign' => $paySign
        ];
    }
}